#define GUI_SDL_EMU
#include "gui_sdl.h"
#include "calc.h"

#include "gif.h"
#include "gifhandle.h"

#define BIG_WINDOWS_ICON 0
#define max(a, b)  (((a) > (b)) ? (a) : (b))
#define min(a, b)  (((a) < (b)) ? (a) : (b))
bool gif_anim_advance;
bool silent_mode = false;
#ifdef SDLWabbitemu_DEBUG_BENCHMARKING
#warning SDLWabbitemu benchmarking is enabled - this option will slow down the emulator.
// NOTE: This is not changeable yet!
bool BENCHMARKING = true;
bool ENABLE_GUI_UPDATING = true;
bool ENABLE_CALC_EMULATION = true;
int BENCHMARKING_HIGHEST_EMU_TIME, BENCHMARKING_LOWEST_EMU_TIME = 0;
int BENCHMARKING_HIGHEST_GUI_TIME, BENCHMARKING_LOWEST_GUI_TIME = 0;
#endif
//bool DEBUG = true;
int prevCalcScale;
LPCALC lpCalc;

bool start_main()
{
	/*printf("TPF: %d\n", TPF);
	//memset(frames, 0, sizeof(frames));
	lpCalc = calc_slot_new();
	lpCalc->SkinEnabled = false;
	//MyFrame *frame;
	int result = rom_load(lpCalc, lpCalc->rom_path);
	if (result == TRUE) {
		//frame = gui_frame(lpCalc);
	} else {
		calc_slot_free(lpCalc);
		//char *filePath = LoadRomIntialDialog();
		char *filePath = "ti83p_112.rom";
		//wxMessageBox(string, wxT("OnInit"), wxOK, NULL);
		//printf_d("hello", string);
		if (filePath != NULL) {
			lpCalc = calc_slot_new();
			result = rom_load(lpCalc, filePath);
			//delete filePath;
			if (result == TRUE) {
				//frame = gui_frame(lpCalc);
				initLCD();
				lpCalc->scale = 2;
				//lpCalc->active = TRUE;
				lpCalc->running = TRUE;
			} else {
				printf("[SDLWabbitemu] [start_main] Failed to load ROM!\n");
				return FALSE;
			}
		} else {
			return FALSE;
		}
	}*/
	//frame->SetFocus();
	/*timer = new wxTimer();
	timer->Connect(wxEVT_TIMER, (wxObjectEventFunction) &MyApp::OnTimer);
	timer->Start(FPS, false);*/
	//timer = SDL_AddTimer(FPS, OnTimer, NULL);
	
	printf("[SDLWabbitemu] [start_main] Successfully started emulation!\n");
	return TRUE;
}

unsigned GetTickCount()
{
		struct timeval tv;
		if(gettimeofday(&tv, NULL) != 0)
			return 0;

		return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}

int getNumOfActiveCalcs() {
	int active_calcs = 0;
	for (int i = 0; i < MAX_CALCS; i++) {
		if (calcs[i].active) {
			active_calcs++;
		}
	}
	return active_calcs;
}

void printf_d( const char* format, ... ) {
#ifdef SDLWabbitemu_DEBUG_OUTPUT
	va_list args;
	//fprintf( stdout, "Error: " );
	va_start( args, format );
	vfprintf( stdout, format, args );
	va_end( args );
#endif
}

struct timeval initMSTimer() {
	struct timeval start;
	gettimeofday(&start, NULL);
	return start;
}

int getMSFromTimer(struct timeval startTime) {
	struct timeval end;
	long mtime,seconds,useconds;
	gettimeofday(&end, NULL);
	
	seconds = end.tv_sec - startTime.tv_sec;
	useconds = end.tv_usec - startTime.tv_usec;
	//printf_d("[getMSFromTimer] DEBUG: seconds = %ld useconds = %ld end.tv_usec = %ld \n", seconds, useconds, end.tv_usec);
	mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
	
	return mtime;
}

void OnTimer() {
	//printf_d("[SDLWabbitemu] [OnTimer] Function called!\n");
	static int difference;
	static unsigned prevTimer;
	unsigned dwTimer = GetTickCount();
	//unsigned dwTimer = SDL_GetTicks();
	#ifdef SDLWabbitemu_DEBUG_BENCHMARKING
	struct timeval start;
	#endif
	
	// How different the timer is from where it should be
	// guard from erroneous timer calls with an upper bound
	// that's the limit of time it will take before the
	// calc gives up and claims it lost time
	difference += ((dwTimer - prevTimer) & 0x003F) - TPF;
	prevTimer = dwTimer;
	//printf_d("%d", difference);

	// Are we greater than Ticks Per Frame that would call for
	// a frame skip?
	if (difference > -TPF) {
		#ifdef SDLWabbitemu_DEBUG_BENCHMARKING
		// NOTE: This timer may screw up TPF counting!
		if (BENCHMARKING) gettimeofday(&start, NULL);
		if (ENABLE_CALC_EMULATION)
		#endif
		calc_run_all();
		
		#ifdef SDLWabbitemu_DEBUG_BENCHMARKING
		if (BENCHMARKING) printf_d("[SDLWabbitemu] [B] [MyApp::OnTimer] Runtime for calc_run_all() is %lu ms \n", getMSFromTimer(start));
		if (BENCHMARKING) {
			if ((BENCHMARKING_HIGHEST_EMU_TIME < (int)getMSFromTimer(start)) || ((BENCHMARKING_HIGHEST_EMU_TIME == 0 && ENABLE_CALC_EMULATION == 1)))
				BENCHMARKING_HIGHEST_EMU_TIME = (int)getMSFromTimer(start);
			if ((BENCHMARKING_LOWEST_EMU_TIME > (int)getMSFromTimer(start)) || ((BENCHMARKING_LOWEST_EMU_TIME == 0) && ENABLE_CALC_EMULATION == 1))
				BENCHMARKING_LOWEST_EMU_TIME = (int)getMSFromTimer(start);
		}
		#endif
		while (difference >= TPF) {
			calc_run_all();
			difference -= TPF;
		}

		int i;
		#ifdef SDLWabbitemu_DEBUG_BENCHMARKING
		if (BENCHMARKING) gettimeofday(&start, NULL);
		if (ENABLE_GUI_UPDATING)
		#endif
		for (i = 0; i < MAX_CALCS; i++) {
			if (calcs[i].active) {
				//frames[i]->gui_draw();
				gui_draw();
			}
		}
		#ifdef SDLWabbitemu_DEBUG_BENCHMARKING
		if (BENCHMARKING) printf_d("[SDLWabbitemu] [B] [MyApp::OnTimer] Runtime for frames[i]->gui_draw() is %lu ms \n", getMSFromTimer(start));
		if (BENCHMARKING) {
			if ((BENCHMARKING_HIGHEST_GUI_TIME < (int)getMSFromTimer(start)) || ((BENCHMARKING_HIGHEST_GUI_TIME == 0 && ENABLE_GUI_UPDATING == 1)))
				BENCHMARKING_HIGHEST_GUI_TIME = (int)getMSFromTimer(start);
			if ((BENCHMARKING_LOWEST_GUI_TIME > (int)getMSFromTimer(start)) || ((BENCHMARKING_LOWEST_GUI_TIME == 0) && ENABLE_GUI_UPDATING == 1))
				BENCHMARKING_LOWEST_GUI_TIME = (int)getMSFromTimer(start);
		}
		#endif
	// Frame skip if we're too far ahead.
	} else {
		difference += TPF;
	}
	return 0;
}

void gui_draw() {
	#ifdef SDLWabbitemu_DEBUG_BENCHMARKING
	struct timeval start;
	if (BENCHMARKING) gettimeofday(&start, NULL);
	#endif
	//wxLCD->Refresh();
	LCDOnPaint();
	#ifdef SDLWabbitemu_DEBUG_BENCHMARKING
	if (BENCHMARKING) printf_d("[SDLWabbitemu] [B] [gui_draw] Runtime for LCDOnPaint(); is %lu ms \n", getMSFromTimer(start));
	if (BENCHMARKING) gettimeofday(&start, NULL);
	#endif

	if (lpCalc->gif_disp_state != GDS_IDLE) {
		static int skip = 0;
		if (skip == 0) {
			gif_anim_advance = true;
			//this->Update();
		}
		skip = (skip + 1) % 4;
	}
	#ifdef SDLWabbitemu_DEBUG_BENCHMARKING
	if (BENCHMARKING) printf_d("[SDLWabbitemu] [B] [gui_draw] Runtime for GIF handling is %lu ms \n", getMSFromTimer(start));
	#endif
}

/*MyFrame *gui_frame(LPCALC lpCalc) {
	if (!lpCalc->scale)
		lpCalc->scale = 2; //Set original scale
	
	MyFrame *mainFrame = new MyFrame(lpCalc);
	mainFrame->Show(true);
	frames[lpCalc->slot] = mainFrame;

	mainFrame->wxLCD = new MyLCD(mainFrame, lpCalc);
	mainFrame->wxLCD->Show(true);
	lpCalc->running = TRUE;
	mainFrame->SetSpeed(100);
	
	mainFrame->Centre(0);   //Centres the frame
	
	mainFrame->gui_frame_update();
	return mainFrame;
}*/

void gui_frame_update() {
	//wxMenuBar *wxMenu = this->GetMenuBar();
	//lpCalc->calcSkin = wxGetBitmapFromMemory(TI_83p);
	//lpCalc->keymap = wxGetBitmapFromMemory(TI_83p_Keymap).ConvertToImage();
	int skinWidth, skinHeight, keymapWidth, keymapHeight;
	
	/*if (lpCalc->calcSkin.IsOk()) {
		skinWidth = 350;//lpCalc->calcSkin.GetWidth();
		skinHeight = 725;//lpCalc->calcSkin.GetHeight();
	}
	if (lpCalc->keymap.IsOk()) {
		keymapWidth = 350;//lpCalc->keymap.GetWidth();
		keymapHeight = 725;//lpCalc->keymap.GetHeight();
	}*/
	int foundX = 0, foundY = 0;
	bool foundScreen = false;
	if (((skinWidth != keymapWidth) || (skinHeight != keymapHeight)) && skinHeight > 0 && skinWidth > 0) {
		lpCalc->SkinEnabled = false;
		printf("[SDLWabbitemu] [gui_frame_update] Skin and Keymap are not the same size! \n");
		//wxMessageBox(wxT("Skin and Keymap are not the same size"), wxT("Error"),  wxOK, NULL);
	} else {
		//lpCalc->SkinSize.SetWidth(skinWidth);
		//lpCalc->SkinSize.SetHeight(skinHeight);		//find the screen size
		for(int y = 0; y < skinHeight && foundScreen == false; y++) {
			for (int x = 0; x < skinWidth && foundScreen == false; x++) {
				//if (lpCalc->keymap.GetBlue(x, y) == 0 &&
						//lpCalc->keymap.GetRed(x, y) == 255 &&
						//lpCalc->keymap.GetGreen(x, y) == 0) {
					//81 92
					foundX = x;
					foundY = y;
					foundScreen = true;
				//}
			}
		}
		//lpCalc->LCDRect.SetLeft(foundX);
		//lpCalc->LCDRect.SetTop(foundY);
		/*do {
			foundX++;
		} while (lpCalc->keymap.GetBlue(foundX, foundY) == 0 &&
				lpCalc->keymap.GetRed(foundX, foundY) == 255 &&
				lpCalc->keymap.GetGreen(foundX, foundY) == 0);
		lpCalc->LCDRect.SetRight(foundX--);
		do {
			foundY++;
		}while (lpCalc->keymap.GetBlue(foundX, foundY) == 0 &&
				lpCalc->keymap.GetRed(foundX, foundY) == 255 &&
				lpCalc->keymap.GetGreen(foundX, foundY) == 0);
		lpCalc->LCDRect.SetBottom(foundY);*/
	}
	if (!foundScreen) {
		printf("[SDLWabbitemu] [gui_frame_update] Unable to find the screen box!\n");
		//wxMessageBox(wxT("Unable to find the screen box"), wxT("Error"), wxOK, NULL);
		lpCalc->SkinEnabled = false;
	}
	
	//if (wxMenu != NULL) {
		if (!lpCalc->SkinEnabled) {
			//wxMenu->Check(ID_Calc_Skin, false);
			// Create status bar
			//wxStatusBar *wxStatus = this->GetStatusBar();
			//if (wxStatus == NULL)
			//	wxStatus = this->CreateStatusBar(1, wxST_SIZEGRIP, wxID_ANY );
			const int iStatusWidths[] = {100, -1};
			//wxStatus->SetFieldsCount(2, iStatusWidths);
			//wxString wxCalcModelTxt(CalcModelTxt[lpCalc->model], wxConvUTF8);
			//wxStatus->SetStatusText(CalcModelTxt[lpCalc->model], 1);
			//wxStatus->SetStatusText(wxCalcModelTxt, 1);
			char *title = "SDLWabbitemu - ";
			strcat(title, CalcModelTxt[lpCalc->model]);
			
			//wxSize skinSize(128*lpCalc->scale, 64*lpCalc->scale+4); //The +4 is important to show all LCD
			
			//if (wxMenu)
			//	skinSize.IncBy(0, wxSystemSettings::GetMetric(wxSYS_MENU_Y, this));
			
			
			//this->SetClientSize(skinSize);
			//this->SetSize(128 * lpCalc->scale, 64 * lpCalc->scale + 60);
		} else {
			//wxMenu->Check(ID_Calc_Skin, true);
			//wxStatusBar *wxStatus = this->GetStatusBar();
			//if (wxStatus != NULL) {
			//	wxStatus->Destroy();
			//}
			//wxSize skinSize(350, 725);
			//if (wxMenu)
			//	skinSize.IncBy(0, wxSystemSettings::GetMetric(wxSYS_MENU_Y, this));
			//this->SetClientSize(skinSize);
		}
	//}
	
	//this->SendSizeEvent();
}


int gui_debug(LPCALC lpCalc) {
	return 0;
}

void init_gui() {
	LCD_t *lcd = lpCalc->cpu.pio.lcd;
	int scale = lpCalc->scale;
	int draw_width = lcd->width * scale;
	int draw_height = 64 * scale;
}

void SetSpeed(int speed) {
	lpCalc->speed = speed;
	//wxMenuBar *wxMenu = this->GetMenuBar();
}

void OnPauseEmulation() {
	//wxMenuBar *wxMenu = this->GetMenuBar();
	if (lpCalc->running) {
		//Tick is checked and emulation stops
		lpCalc->running = FALSE;
	//	wxMenu->Check(ID_Calc_Pause, true);
	} else {
		//Tick is unchecked and emulation resumes
		lpCalc->running = TRUE;
	//	wxMenu->Check(ID_Calc_Pause, false);
	}
}
